package main

import (
	"encoding/json"
	"fmt"
	"github.com/Alex-zs/my_docker/cgroups"
	"github.com/Alex-zs/my_docker/cgroups/subsystems"
	"github.com/Alex-zs/my_docker/container"
	"github.com/Alex-zs/my_docker/network"
	log "github.com/sirupsen/logrus"
	"math/rand"
	"os"
	"strconv"
	"strings"
	"time"
)

func Run(tty bool, cmdArray []string, res *subsystems.ResourceConfig, volume, containerName, imageName string,
	envSlice []string, networkName string, portMapping []string) {
	containerID := randStringBytes(15)
	if containerName == "" {
		containerName = containerID
	}
	containerProc, writePipe := container.NewParentProcess(tty, volume, containerName, imageName, envSlice)
	if containerProc == nil {
		log.Errorf("new container process error")
		return
	}
	if err := containerProc.Start(); err != nil {
		log.Error(err)
	}

	log.Infof("start container pid %v", containerProc.Process.Pid)


	// set control group
	cgroupManager := cgroups.NewCgroupManager(containerID)
	cgroupManager.Set(res)
	cgroupManager.Apply(containerProc.Process.Pid)

	// config container network
	containerInfo := &container.ContainerInfo{
		Pid: strconv.Itoa(containerProc.Process.Pid),
		Id: containerID,
		Name: containerName,
		PortMapping: portMapping,
		NetworkName: networkName,
	}
	if networkName != "" {
		network.Init()
		if err := network.Connect(networkName, containerInfo); err != nil {
			log.Errorf("error connect network: %v", err)
			return
		}
	}

	// send command
	sendInitCommand(cmdArray, writePipe)

	// record container info
	containerInfo.Command = strings.Join(cmdArray, " ")
	containerInfo.CreatedTime = time.Now().Format("2006-01-02 15:04:05")
	containerInfo.Status = container.RUNNING
	containerInfo.Volume = volume
	err := recordContainerInfo(containerInfo)
	if err != nil {
		log.Errorf("record container info err")
		return
	}

	// wait until container exit
	if tty {
		containerProc.Wait()
		deleteContainerInfo(containerName)
		container.DeleteWorkSpace(volume, containerName)
		defer cgroupManager.Destroy()
	}
}

func sendInitCommand(cmdArray []string, writePipe *os.File)  {
	command := strings.Join(cmdArray, " ")
	log.Infof("command all is: %s", command)
	writePipe.WriteString(command)
	writePipe.Close()
}

func recordContainerInfo(containerInfo *container.ContainerInfo) error {
	jsonBytes, err := json.Marshal(containerInfo)
	if err != nil {
		log.Errorf("json marshal error %v", err)
		return err
	}
	jsonStr := string(jsonBytes)
	dirUrl := fmt.Sprintf(container.DefaultInfoLocation, containerInfo.Name)
	if err := os.MkdirAll(dirUrl, 0622); err != nil {
		log.Errorf("Mkdir error %s error %v", dirUrl, err)
		return err
	}

	fileName := dirUrl + "/" + container.ConfigName
	configFile, err := os.Create(fileName)
	defer configFile.Close()
	if err != nil {
		log.Errorf("create file %s error %v", fileName, err)
		return err
	}

	if _, err := configFile.WriteString(jsonStr); err != nil {
		log.Errorf("file write error %v", err)
	}
	return nil
}


func deleteContainerInfo(containerName string) {
	dirURL := fmt.Sprintf(container.DefaultInfoLocation, containerName)
	if err := os.RemoveAll(dirURL); err != nil {
		log.Errorf("Remove dir %s error %v", dirURL, err)
	}
}
func randStringBytes(n int)	string {
	letters := "0123456789"
	rand.Seed(time.Now().UnixNano())
	b := make([]byte, n)
	for i := range b {
		b[i] = letters[rand.Intn(len(letters))]
	}
	return string(b)
}
